import os
import struct
import mmap
import user

import Utils.Utils
import DataExtraction.BSAIndexEntry
import DataExtraction.BSAIndex
import DataExtraction.BSAVOC
import DataExtraction.BSAPalette
import DataExtraction.BSAFont

class GlobalBSA:
    CODE_TABLE =\
        ["AB89EFCD23016745",  # [address % 16] == 0
        "CDEF89AB45670123",
        "0123456789ABCDEF",
        "0123456789ABCDEF",
        "DCFE98BA54761032",  # [address % 16] == 4
        "EFCDAB8967452301",
        "EFCDAB8967452301",
        "0123456789ABCDEF",
        "23016745AB89EFCD",  # [address % 16] == 8
        "45670123CDEF89AB",
        "89ABCDEF01234567",
        "89ABCDEF01234567",
        "54761032DCFE98BA",  # [address % 16] == 12
        "67452301EFCDAB89",
        "67452301EFCDAB89",
        "89ABCDEF01234567"]

    NAME_LENGTH = 12
    SIZE_LENGTH = 2
    UNKNOWNS_LENGTH = 2
    INVERSE_CODE_TABLE_SIZE = 16
    MMAP_FLAGS = mmap.MAP_PRIVATE
    GLOBAL_BSA_FILENAME = 'GLOBAL.BSA'
    
    def __init__(self, base_dir = ''):
        self._number_of_entries = 0
        self._file_length = 0
        self._index = []

        self._base_dir = base_dir
        self._global_bsa_filename = os.path.join(base_dir, self.GLOBAL_BSA_FILENAME)
        self._global_bsa = open(self._global_bsa_filename, 'rb')
        
        self.get_file_size()

        self._global_bsa_data = mmap.mmap(self._global_bsa.fileno(), self._file_length,
                                          self.MMAP_FLAGS)
        
        self.read_number_of_index_entries()
        
        # 12 for the name + 2 for size + 4 unknown
        self._index_item_size =\
            self.NAME_LENGTH + self.SIZE_LENGTH + self.UNKNOWNS_LENGTH * 2
            
        self._index_size = self._number_of_entries * self._index_item_size
        self._index_offset = self._file_length - self._index_size
        
        self._idx = DataExtraction.BSAIndex.BSAIndex(self._number_of_entries,
                                                     self._global_bsa_data,
                                                     self._file_length)
        
        self.create_inverse_code_table()

        self.make_inverse_code_table()
    
    def __getitem__(self, key):
        return self._global_bsa_data[key]
    
    def __repr__(self):
        return self.pretty_print()
        
    def ugly_print(self):
        index_string = str(self._idx)   
        return "GlobalBSA: [filename=" + self._global_bsa_filename +\
            "|number of entries=" + str(self._number_of_entries) + "|index=" +\
            index_string + "|inverse_code_table=" + str(self._inverse_code_table) + "]"

    def pretty_print(self):
        index_string = '\n'
        
        for entry_name in sorted(self._idx):
            entry = self._idx.get_entry(entry_name)
            index_string += str(entry) + '\n'

        index_string += '\n'

        return "GlobalBSA: [filename=" + self._global_bsa_filename +\
            "|number of entries=" + str(self._number_of_entries) + "|index=" +\
            index_string +  "|inverse_code_table=" + str(self._inverse_code_table) + "]"
        
    def create_inverse_code_table(self):
        self._inverse_code_table =\
            [ [None for x in xrange(self.INVERSE_CODE_TABLE_SIZE)]
             for y in xrange(self.INVERSE_CODE_TABLE_SIZE) ]
    
    def make_inverse_code_table(self):
        for y in xrange(self.INVERSE_CODE_TABLE_SIZE):
            for x in xrange(self.INVERSE_CODE_TABLE_SIZE):
                inv = int(self.CODE_TABLE[y][x], 16)
                
                self._inverse_code_table[y][inv] = x
        
    def rewind(self):
        self._global_bsa.seek(0, os.SEEK_SET)
        
    def fast_forward(self):
        self._global_bsa.seek(0, os.SEEK_END)
        
    def parse_index(self):
        self._idx.load()
        
    def load(self):
        self._load_resources()
        
    def get_file_size(self):
        self._file_length = os.path.getsize(self._global_bsa_filename)
        
    def read_number_of_index_entries(self):
        # read the first 16 bits (2 bytes), the number of 
        # index entries on the file
        #twobytes = self._global_bsa.read(2)
        twobytes = self._global_bsa_data[:2]
        self._number_of_entries = struct.unpack('H', twobytes)[0]

    def get_voc_entries(self):
        return self._idx.get_voc_entries()
    
    def get_cfa_entries(self):
        return self._idx.get_cfa_entries()

    def get_dfa_entries(self):
        return self._idx.get_dfa_entries()
    
    def get_cif_entries(self):
        return self._idx.get_cif_entries()
    
    def get_global_bsa_data(self):
        return self._global_bsa_data
    
    def _load_resources(self):
        self.parse_index()